using System;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Shapes;
using System.Collections.Generic;
using System.Diagnostics;
using Microsoft.Win32;
using System.IO;
using System.Xml.Serialization;
using System.Threading;
using System.Windows.Threading;
using WozLib.Net;
using WozLib.UI.Ink;
using System.Windows.Input;
using WozLib.UI.Dialogs;
using WozLib.UI.Ink.SketchCommands;
using WozLib.UI.Ink.SketchObjects;
using WozLib.UI.Ink.SketchElements;
using WozLib.UI.Ink.SketchProperties;


namespace ClientTestUI
{
  /// <summary>
  /// Interaction logic for ClientWindow.xaml
  /// </summary>

  public partial class ClientWindow : Window
  {
    private SketchCommandCollection _WozClientCommands = new SketchCommandCollection();

    private SketchCommandCollection _PendingWozClientCommands = new SketchCommandCollection();

    private WozClient _WozClient;

    private SketchCommandReferences _ClientCommandReferences;

    private SketchObjectRegistry _WozObjectRegistry = new SketchObjectRegistry();

    private string _ServerAddrString = "localhost";

    public string ServerAddrString
    {
      get { return _ServerAddrString; }
      set { _ServerAddrString = value; }
    }

    
    public ClientWindow()
    {
      CommandBinding closeCommandBinding = new CommandBinding(ApplicationCommands.Close);
      closeCommandBinding.CanExecute += new CanExecuteRoutedEventHandler(closeCommandBinding_CanExecute);
      closeCommandBinding.Executed += new ExecutedRoutedEventHandler(closeCommandBinding_Executed);
      CommandBindings.Add(closeCommandBinding);

      InitializeComponent();

      // Don't touch any interface components until load()"
      this.Loaded += new RoutedEventHandler(ClientWindow_Loaded);
      this.SizeChanged += new SizeChangedEventHandler(ClientWindow_SizeChanged);
      
      List<string> editingModes = new List<string>();
      //editingModes.Add(InkCanvasEditingMode.Ink);
      //editingModes.Add(InkCanvasEditingMode.InkAndGesture);
      //editingModes.Add(InkCanvasEditingMode.GestureOnly);
      //editingModes.Add(InkCanvasEditingMode.Select);
      //editingModes.Add(InkCanvasEditingMode.EraseByPoint);
      //editingModes.Add(InkCanvasEditingMode.EraseByStroke);
      //editingModes.Add(InkCanvasEditingMode.None);
      //editingModes.Add("Ink");
      //editingModes.Add("InkAndGesture");
      //editingModes.Add("GestureOnly");
      //editingModes.Add("Select");
      //editingModes.Add("EraseByPoint");
      //editingModes.Add("EraseByStroke");
      //editingModes.Add("None");
      //_EditMode.Items.Add("Ink");
      //_EditMode.Items.Add("InkAndGesture");
      //_EditMode.Items.Add("GestureOnly");
      //_EditMode.Items.Add("Select");
      //_EditMode.Items.Add("EraseByPoint");
      //_EditMode.Items.Add("EraseByStroke");
      //_EditMode.Items.Add("None");
      //_EditMode.ItemsSource = editingModes;
      //SelectedItem="{Binding ElementName=_ClientInkCanvas, Path=EditingMode}"
    }

    void closeCommandBinding_Executed(object sender, ExecutedRoutedEventArgs e)
    {
      this.Close();
    }

    void closeCommandBinding_CanExecute(object sender, CanExecuteRoutedEventArgs e)
    {
      e.CanExecute = true;
    }

    void ClientWindow_SizeChanged(object sender, SizeChangedEventArgs e)
    {
      // Let's ignore client resizing of the window

      //Size newSize = e.NewSize;
      //Size previousSize = e.PreviousSize;
      //_ClientInkCanvas.Width = newSize.Width;
      //_ClientInkCanvas.Height = newSize.Height;
      //CommandOccured(new WozWindowResizeCommand(newSize,previousSize));
    }

    void ClientWindow_Loaded(object sender, RoutedEventArgs e)
    {
      this.KeyDown += new KeyEventHandler(ClientWindow_KeyDown);

      // for now let's assume the client cannot resize
      this.ResizeMode = ResizeMode.NoResize;

      //_FullScreenRadio.Click += new RoutedEventHandler(_FullScreenRadio_Click);
      _FullScreenCheck.Unchecked += new RoutedEventHandler(_FullScreenCheck_Unchecked);
      _FullScreenCheck.Checked += new RoutedEventHandler(_FullScreenCheck_Checked);

      // Set references for executing ClientCommands
      _ClientCommandReferences = new SketchCommandReferences();
      _ClientCommandReferences.InkCanvas = _ClientInkCanvas;
      _ClientCommandReferences.InFrontInkCanvas = _InkOverlayCanvas;
      _ClientCommandReferences.ClientWindow = new WozLib.UI.Containers.WozSizeable(_ClientInkCanvas, this);
      _ClientCommandReferences.InkOverlayCanvas = _InkOverlayCanvas;
      _ClientCommandReferences.WozObjectRegistry = _WozObjectRegistry;

      // we'll capture some mouse movements
      _ClientInkCanvas.MouseMove += new System.Windows.Input.MouseEventHandler(_ClientInkCanvas_MouseMove);
      _ClientInkCanvas.SketchObjectRegistry = _WozObjectRegistry;
      _ToolSelectToolbar.UserToolsEnabledCollection = new List<SketchPad.InkTools>(new SketchPad.InkTools[] 
        {  
          SketchPad.InkTools.Eraser,
          SketchPad.InkTools.Pencil
        });
      _ClientInkCanvas.UserToolsEnabledCollectionChanged += new SketchInkCanvas.UserToolsEnabledCollectionChangedHandler(_ClientInkCanvas_UserToolsEnabledCollectionChanged);
      _ClientInkCanvas.UserCurrentInkToolChanged += new SketchInkCanvas.UserCurrentInkToolChangedHandler(_ClientInkCanvas_UserCurrentInkToolChanged);
      _ToolSelectToolbar.SelectedToolChanged += new WozLib.UI.Controls.ToolSelectToolbar.SelectedToolChangedHandler(_ToolSelectToolbar_SelectedToolChanged);

      SketchButton.SketchButtonCreated += new SketchButton.SketchButtonCreatedDelegate(SketchButton_SketchButtonCreated);
      SketchRectangle.LinkCreated += new SketchRectangle.LinkCreatedDelegate(SketchRectangle_LinkCreated);

      // try to connect to the server
      //try
      //{
      //  _WozClient = new WozClient("ServerAddrString");

      //  _WozClient.Start();

      //  _WozClient.WozClientCommandsReceived += new WozClient.WozClientCommandsReceivedDelegate(_WozClient_WozClientCommandsReceived);

      //  if (!_WozClient.Connected)
      //  {
      //    Debug.WriteLine("Couldn't connect to server");
      //    TextMessageDialog textMessageDialog = new TextMessageDialog("Couldn't Connect to Server", "Couldn't Connect to Server", "OK");
      //    textMessageDialog.ShowDialog();
      //    _WozClient = null;
      //  }
      //  else
      //  {
      //    lock (_PendingWozClientCommands)
      //    {
      //      bool commandsSent = _WozClient.SendWozClientCommands(_PendingWozClientCommands);
      //      if (commandsSent)
      //      {
      //        _PendingWozClientCommands.Clear();
      //      }
      //    }
      //  }
      //}
      //catch (Exception ex)
      //{
      //  Debug.WriteLine("Couldn't connect to server");
      //  TextMessageDialog textMessageDialog = new TextMessageDialog("Couldn't Connect to Server", "Couldn't Connect to Server", "OK");
      //  textMessageDialog.ShowDialog();
      //}

      // Subscribe to the command stream of our client canvas
      _ClientInkCanvas.CommandOccured += new ClientInkCanvas.CommandOccuredDelegate(_ClientInkCanvas_CommandOccured);

      // setup the text to speach stuff
      //SpeechSynthesizer _speechSynthesizer = new SpeechSynthesizer();
      //_speechSynthesizer.Rate = 2;
      //PromptBuilder pb = new PromptBuilder();
      //pb.StartVoice("Microsoft Sam");
      //pb.AppendText("Hello there, I'm anna. Hello. My name is computer. I don't have much to say!");
      //pb.EndVoice();
      //_speechSynthesizer.Speak(pb);
      //_speechSynthesizer.Speak("My name is " + _speechSynthesizer.Voice.Name);

      //_speechSynthesizer.Speak("Hello. My name is computer. I don't have much to say!");

      //SpeechRecognizer _recognizer = new SpeechRecognizer();

      ////Create a pizza grammar
      //Choices pizzaChoices = new Choices();

      //pizzaChoices.Add(new string[] {"I'd like a cheese pizza",
      //  "I'd like a pepperoni pizza",
      //  "I'd like a large pepperoni pizza",
      //  "I'd like a small thin crust vegetarian pizza",
      //  "Hello",
      //  "Testing"
      //});
      ////GrammarBuilder gb = new GrammarBuilder(pizzaChoices);
      //GrammarBuilder gb = new GrammarBuilder("hello");
      //Grammar pizzaGrammar =
      //    new Grammar(gb);

      ////Attach an event handler
      //pizzaGrammar.SpeechRecognized += new EventHandler<SpeechRecognizedEventArgs>(pizzaGrammar_SpeechRecognized);

      //_recognizer.LoadGrammar(pizzaGrammar);


    }

    void SketchRectangle_LinkCreated(SketchRectangle sketchRectangle)
    {
      sketchRectangle.MouseDown += new MouseButtonEventHandler(sketchRectangle_MouseDown);
    }

    void sketchRectangle_MouseDown(object sender, MouseButtonEventArgs e)
    {
      
      
      // capture user clicks and send them across the wire
      if (sender is SketchRectangle && 
        (sender as SketchRectangle)._WozPropertyCollection.HasProperty(WozPropertyType.LinkedScreenID))
      {

        LinkActionSketchCommand linkActionSketchCommand = new LinkActionSketchCommand(
          (sender as SketchRectangle).GetWozObject());

        CommandOccured(linkActionSketchCommand);
      }
    }

    void SketchButton_SketchButtonCreated(SketchButton sketchButton)
    {
      sketchButton.Button.Click += new RoutedEventHandler(Button_Click);
    }

    void Button_Click(object sender, RoutedEventArgs e)
    {
      // capture user clicks and send them across the wire
      if (sender is Button && (sender as Button).Parent is SketchButton)
      {
        UserButtonActionSketchCommand userButtonActionSketchCommand = new UserButtonActionSketchCommand(
          ((sender as Button).Parent as SketchButton).GetWozObject());

        CommandOccured(userButtonActionSketchCommand);
      }
    }

    private bool _SettingCurrentTool = false;

    void _ToolSelectToolbar_SelectedToolChanged(SketchPad.InkTools tool)
    {
      if (!_SettingCurrentTool)
      {
        _SettingCurrentTool = true;
        _ClientInkCanvas.UserCurrentInkTool = tool;
        _ClientInkCanvas_CommandOccured(new ModifyUserCurrentInkTool(tool));
        _SettingCurrentTool = false;
      }
    }

    void _ClientInkCanvas_UserCurrentInkToolChanged(SketchPad.InkTools newInkTool, SketchPad.InkTools previousInkTool)
    {
      if (!_SettingCurrentTool)
      {
        _SettingCurrentTool = true;
        _ToolSelectToolbar.SelectTool(newInkTool);
        _SettingCurrentTool = false;
      }
    }

    void _ClientInkCanvas_UserToolsEnabledCollectionChanged(List<SketchPad.InkTools> newInkToolCollection, List<SketchPad.InkTools> previousInkToolCollection)
    {
      _ToolSelectToolbar.UserToolsEnabledCollection = newInkToolCollection;
    }

    void Hide_Click(object sender, RoutedEventArgs e)
    {
      ToggleToolbar();
    }

    private void ToggleToolbar()
    {
      if (_ToolBarTray.Visibility == Visibility.Visible)
      {
        _ToolBarTray.Visibility = Visibility.Collapsed;
      }
      else
      {
        _ToolBarTray.Visibility = Visibility.Visible;
      }
    }

    void ClientWindow_KeyDown(object sender, KeyEventArgs e)
    {
      switch (e.Key)
      {
        case Key.F1:
          ToggleToolbar();
          break;
        case Key.F10:
          goto case Key.F1;
        case Key.F11:
          goto case Key.F1;
        case Key.F12:
          goto case Key.F1;
        case Key.F13:
          goto case Key.F1;
        case Key.F14:
          goto case Key.F1;
        case Key.F15:
          goto case Key.F1;
        case Key.F16:
          goto case Key.F1;
        case Key.F17:
          goto case Key.F1;
        case Key.F18:
          goto case Key.F1;
        case Key.F19:
          goto case Key.F1;
        case Key.F2:
          goto case Key.F1;
        case Key.F20:
          goto case Key.F1;
        case Key.F21:
          goto case Key.F1;
        case Key.F22:
          goto case Key.F1;
        case Key.F23:
          goto case Key.F1;
        case Key.F24:
          goto case Key.F1;
        case Key.F3:
          goto case Key.F1;
        case Key.F4:
          goto case Key.F1;
        case Key.F5:
          goto case Key.F1;
        case Key.F6:
          goto case Key.F1;
        case Key.F7:
          goto case Key.F1;
        case Key.F8:
          goto case Key.F1;
        case Key.F9:
          goto case Key.F1;
        default:
          break;
      }
    }

    void _FullScreenCheck_Unchecked(object sender, RoutedEventArgs e)
    {
      //this.BorderThickness = new Thickness(2.0);
      this.WindowStyle = WindowStyle.SingleBorderWindow;
      this.WindowState = WindowState.Normal;
      this.SizeToContent = SizeToContent.WidthAndHeight;
      //this.ResizeMode = ResizeMode.NoResize;
      double scaleX = 1.0;
      double scaleY = 1.0;
      //_ClientInkCanvas.LayoutTransform = new ScaleTransform(scaleX, scaleY);
      _InkOverlayCanvas.LayoutTransform = new ScaleTransform(scaleX, scaleY);
    }

    void _FullScreenCheck_Checked(object sender, RoutedEventArgs e)
    {
      //this.BorderThickness = new Thickness(0.0);
      double oldWidth = _ClientInkCanvas.Width;
      double oldHeight = _ClientInkCanvas.Height;
      this.SizeToContent = SizeToContent.Manual;
      this.WindowStyle = WindowStyle.None;
      this.WindowState = WindowState.Maximized;
      //this.ResizeMode = ResizeMode.NoResize;
      double scaleX = this.RenderSize.Width / oldWidth;
      double scaleY = this.RenderSize.Height / oldHeight;
      
      //_ClientInkCanvas.LayoutTransform = new ScaleTransform(scaleX, scaleY);
      _InkOverlayCanvas.LayoutTransform = new ScaleTransform(scaleX, scaleY);
    }

    //void pizzaGrammar_SpeechRecognized(object sender, SpeechRecognizedEventArgs e)
    //{
    //  SpeechSynthesizer _speechSynthesizer = new SpeechSynthesizer();
    //  _speechSynthesizer.Speak(e.Result.Text);
    //}

    void _ClientInkCanvas_MouseMove(object sender, System.Windows.Input.MouseEventArgs e)
    {
      // let's push a mouse move command so we can see where the mouse is on the other screen
      MouseMoveSketchCommand wozMouseMoveCommand = new MouseMoveSketchCommand(e.GetPosition(_ClientInkCanvas), e.LeftButton, e.MiddleButton, e.RightButton);
      CommandOccured(wozMouseMoveCommand);
    }

    void _WozClient_WozClientCommandsReceived(SketchCommandCollection wozClientCommands)
    {
      wozClientCommands.SetCommandReferences(_ClientCommandReferences);
      _WozClientCommands.AddWozClientCommand(wozClientCommands.WozClientCommandList);
      RunCommands(wozClientCommands);
    }

    public void RunCommands(SketchCommandCollection wozClientCommands)
    {
      this.Dispatcher.Invoke(System.Windows.Threading.DispatcherPriority.Normal, new RunCommandsDelgate(RunCommandsInternal), wozClientCommands);
    }

    private delegate void RunCommandsDelgate(SketchCommandCollection wozClientCommands);

    bool _RunningServerChanges = false;
    private void RunCommandsInternal(SketchCommandCollection wozClientCommands)
    {
      _RunningServerChanges = true;
      wozClientCommands.RedoAll();

      // HACK HACK HACK HACK
      if (_ClientInkCanvas.UserCurrentInkTool == SketchPad.InkTools.None)
      {
        _ClientInkCanvas.EditingMode = InkCanvasEditingMode.None;
      }
      else if (_ClientInkCanvas.UserCurrentInkTool == SketchPad.InkTools.Pencil)
      {
        _ClientInkCanvas.EditingMode = InkCanvasEditingMode.Ink;
      }
      else if (_ClientInkCanvas.UserCurrentInkTool == SketchPad.InkTools.Eraser)
      {
        _ClientInkCanvas.EditingMode = InkCanvasEditingMode.EraseByStroke;
      }

      _RunningServerChanges = false;
    }

    void _ClientInkCanvas_CommandOccured(SketchCommand wozClientCommand)
    {
      CommandOccured(wozClientCommand);
    }

    private void CommandOccured(SketchCommand wozClientCommand)
    {
      if (!_RunningServerChanges){
        lock (_PendingWozClientCommands)
        {
          //Debug.WriteLine("Client command: " + wozClientCommand.ToString());
          wozClientCommand.WozClientCommandReferences = _ClientCommandReferences;

          _WozClientCommands.AddWozClientCommand(wozClientCommand);

          bool commandSent = false;

          if (_WozClient != null)
          {
            commandSent = _WozClient.SendWozClientCommands(new SketchCommandCollection(wozClientCommand));
          }

          if (!commandSent)
          {
            // put in pending command list

            _PendingWozClientCommands.AddWozClientCommand(wozClientCommand);
          }
        }
      }
    }

    /// <summary>
    /// if the calling textbox is not focused, this method focuses the textbox
    /// and selects all the text. One would use this method as the PreviewMouseDown
    /// for textboxes where they want the user to just be able to type a new
    /// value after the textbox is clicked once.
    /// 
    /// We cannot use GotFocus because the click handler will unselect the text and
    /// place the cursor at the click point
    /// </summary>
    /// <param name="sender"></param>
    /// <param name="e"></param>
    protected void _TextBox_SelectAll_PreviewMouseDown(object sender, MouseButtonEventArgs e)
    {
      // if it was a textbox that was clicked and its not selected
      if (sender is TextBox && !(sender as TextBox).IsFocused)
      {
        // select all the text
        (sender as TextBox).SelectAll();

        // give the textbox input focus
        (sender as TextBox).Focus();

        // set handled to true to prevent the regular textbox click code from running
        e.Handled = true;
      }
    }

    /// <summary>
    /// this method focuses the textbox and selects all the text. 
    /// One would use this method as the GotFocus
    /// for textboxes where they want the user to just be able to type a new
    /// value after the textbox is tabbed into.
    /// </summary>
    /// <param name="sender"></param>
    /// <param name="e"></param>
    void _TextBox_SelectAll_GotFocus(object sender, RoutedEventArgs e)
    {
      if (sender is TextBox)
      {
        // select all the text
        (sender as TextBox).SelectAll();
      }
    }

    void Connect_Click(object sender, RoutedEventArgs e)
    {
      // Make sure we are not already connected
      if (_WozClient == null || !_WozClient.Connected)
      {
        try
        {
          //_WozClient = new WozClient();
          _WozClient = new WozClient(ServerAddrString);

          _WozClient.Start();

          if (!_WozClient.Connected)
          {
            Debug.WriteLine("Couldn't connect to server");
            TextMessageDialog textMessageDialog = new TextMessageDialog("Couldn't Connect to Server", "Couldn't Connect to Server", "OK");
            textMessageDialog.ShowDialog();
          }
          else
          {
            _ConnectButton.IsEnabled = false;
            _ServerAddrTextBox.IsEnabled = false;
          }

          _WozClient.WozClientCommandsReceived += new WozClient.WozClientCommandsReceivedDelegate(_WozClient_WozClientCommandsReceived);

          lock (_PendingWozClientCommands)
          {
            bool commandsSent = _WozClient.SendWozClientCommands(_PendingWozClientCommands);
            if (commandsSent)
            {
              _PendingWozClientCommands.Clear();
            }
          }
        }
        catch (Exception ex)
        {
          Debug.WriteLine("Couldn't connect to server");
          TextMessageDialog textMessageDialog = new TextMessageDialog("Couldn't Connect to Server", "Couldn't Connect to Server", "OK");
          textMessageDialog.ShowDialog();
        }
      }
    }

    void Save_Click(object sender, RoutedEventArgs e)
    {
      SaveFileDialog saveFileDialog = new SaveFileDialog();
      saveFileDialog.Title = "Where do you want to save the Canvas?";
      saveFileDialog.Filter = "XML|*.xml";

      if (saveFileDialog.ShowDialog() == true)
      {
        SketchCommandCollection toSerialize = _WozClientCommands;

        // Insert code to set properties and fields of the object.
        XmlSerializer mySerializer = new XmlSerializer(typeof(SketchCommandCollection));

        // To write to a file, create a StreamWriter object.
        StreamWriter sw = new StreamWriter(saveFileDialog.FileName);
        mySerializer.Serialize(sw, toSerialize);
        sw.Close();
      }
    }

    void Load_Click(object sender, RoutedEventArgs e)
    {
      OpenFileDialog openFileDialog = new OpenFileDialog();
      openFileDialog.Title = "Where do you want to load the Canvas from?";
      openFileDialog.Filter = "XML|*.xml";

      if (openFileDialog.ShowDialog() == true)
      {
        // Insert code to set properties and fields of the object.
        XmlSerializer mySerializer = new XmlSerializer(typeof(SketchCommandCollection));

        StreamReader sr = new StreamReader(openFileDialog.FileName);

        SketchCommandCollection unserialized = (SketchCommandCollection)mySerializer.Deserialize(sr);

        unserialized.SetCommandReferences(_ClientCommandReferences);

        sr.Close();

        //unserialized.RedoAll();
        if (unserialized.WozClientCommandList.Count > 0)
        {
          _playCommandList = unserialized.WozClientCommandList;
          _playIndex = 0;
          _timer = new DispatcherTimer();
          _timer.Tick += new EventHandler(_timer_Tick);
          _timer.Interval = new TimeSpan(0);
          _timer.Start();
        }
      }
    }

    void _timer_Tick(object sender, EventArgs e)
    {
      if (_playIndex < _playCommandList.Count)
      {
        _playCommandList[_playIndex].Redo();

        _playIndex++;

        if (_playIndex < _playCommandList.Count)
        {
          TimeSpan delta = _playCommandList[_playIndex].Timestamp.Subtract(_playCommandList[_playIndex - 1].Timestamp);
          if (delta.TotalSeconds < 0){
            delta = new TimeSpan(0);
          }
          _timer.Interval = delta; 
          _timer.Start();
        }
      } 
    }

    private DispatcherTimer _timer;
    private List<SketchCommand> _playCommandList;
    private int _playIndex;

    void Ink_Click(object sender, RoutedEventArgs e)
    {
      _ClientInkCanvas.EditingMode = InkCanvasEditingMode.Ink;
    }

    void Gesture_Click(object sender, RoutedEventArgs e)
    {
      _ClientInkCanvas.EditingMode = InkCanvasEditingMode.GestureOnly;
    }

    void InkGesture_Click(object sender, RoutedEventArgs e)
    {
      _ClientInkCanvas.EditingMode = InkCanvasEditingMode.InkAndGesture;
    }

    void Erase_Click(object sender, RoutedEventArgs e)
    {
      _ClientInkCanvas.EditingMode = InkCanvasEditingMode.EraseByStroke;
    }

    void Select_Click(object sender, RoutedEventArgs e)
    {
      _ClientInkCanvas.EditingMode = InkCanvasEditingMode.Select;
    }

    void None_Click(object sender, RoutedEventArgs e)
    {
      _ClientInkCanvas.EditingMode = InkCanvasEditingMode.None;
    }

  }
}